/**
 * 
 */
package com.trungsi.profile;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.aspectj.lang.JoinPoint.StaticPart;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;

/**
 * @author ductrung.tran
 *
 */
public class NotifTracer implements Tracer {

	ConcurrentMap<String, NotifTracker> notifTrackers = new ConcurrentHashMap<String, NotifTracker>();
	
	Thread notifTrackerRemover = new Thread("NotifTrackerRemover") {
		{
			setDaemon(true);
		}
		
		@Override
		public void run() {
			while(true) {
				for (Iterator<Entry<String, NotifTracker>> iter = notifTrackers.entrySet().iterator(); iter.hasNext();) {
					NotifTracker tracker = iter.next().getValue();
					if (System.currentTimeMillis() - tracker.firstStart.longValue() > 120000) {
						iter.remove();
						System.out.println(this.getName() + " : " + tracker);
					}
				}
				try {
					Thread.sleep(120000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	};
	
	{
		notifTrackerRemover.start();
	}
	
	public void traceBegin(ProceedingJoinPoint joinPoint) {
		NotifTracker tracker = getNotifTracker(joinPoint);
		
		tracker.begin(getTraceName(joinPoint));
	}

	private NotifTracker getNotifTracker(ProceedingJoinPoint joinPoint) {
		String notifId = getNotifId(joinPoint);
		
		NotifTracker tracker = getNotifTracker(notifId);
		return tracker;
	}

	private NotifTracker getNotifTracker(String notifId) {
		NotifTracker notifTracker = notifTrackers.putIfAbsent(notifId, new NotifTracker(notifId));
		
		return notifTracker != null ? notifTracker : notifTrackers.get(notifId);
	}

	private String getNotifId(ProceedingJoinPoint joinPoint) {
		return getTraceName(joinPoint);
		//if (traceName.contains("TibSecuredInputReplication")) {
			//Object replicationEvent = getReplicationEventFromArgs(joinPoint.getArgs());
			//return getNotifIdFromReplicationEvent(replicationEvent);
		//}
		
		//return "";
	}

	private String getNotifIdFromReplicationEvent(Object replicationEvent) {
		List<Object> items = getItems(replicationEvent); 
		if (items.isEmpty()) {
			throw new RuntimeException("ReplicationEvent " + replicationEvent + " does not contain any item");
		}
		
		if (items.size() > 1) {
			System.err.println("Warning : ReplicationEvent " + replicationEvent + " contains many items " + items.size());
		}
		
		return getNotifIdFromReplicationEventItem(items.get(0));
	}

	private String getNotifIdFromReplicationEventItem(Object object) {
		return getField("id", object) + "_" + getField("custom_id", object) + "_" + getField("resource", object) + "_" + getField("type", object) + "_" + getField("command", object);
	}

	private Object getField(String fieldName, Object object) {
		Class<?> klass = object.getClass();
		
		try {
			Field field = klass.getField(fieldName);
			return field.get(object);
			
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}

	private List<Object> getItems(Object replicationEvent) {
		return (List<Object>) invokeMethod("getItems", replicationEvent);
	}

	private Object invokeMethod(String methodName, Object obj) {
		Class<?> klass = obj.getClass();
		
		try {
			Method method = klass.getMethod(methodName, null);
			
			return method.invoke(obj, null);
			
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}

	private Object getReplicationEventFromArgs(Object[] args) {
		if (args == null) {
			return null;
		}
		for (Object arg : args) {
			if (arg != null && arg.getClass().getSimpleName().contains("ReplicationEvent")) {
				return arg;
			}
		}
		
		return null;
	}

	public void traceEnd(ProceedingJoinPoint joinPoint) {
		NotifTracker tracker = getNotifTracker(joinPoint);
		
		tracker.end(getTraceName(joinPoint));
		
	}
	
	private String getTraceName(ProceedingJoinPoint joinPoint) {
		Signature signature = joinPoint.getSignature();
		String traceName = signature.getDeclaringTypeName() + 
							"." + signature.getName();
		
		return traceName;
	}

	public void traceBegin(StaticPart staticPart) {
		// TODO Auto-generated method stub
		
	}

	public void traceEnd(StaticPart staticPart) {
		// TODO Auto-generated method stub
		
	}

}
